package com.ejiaoyi.api.controller.v1;

import com.alibaba.fastjson.JSONObject;
import com.ejiaoyi.api.controller.BaseController;
import com.ejiaoyi.api.dto.GetToken;
import com.ejiaoyi.api.dto.RestObject;
import com.ejiaoyi.api.dto.v1.BidderDecrypRecordInfo;
import com.ejiaoyi.api.dto.v1.ChainBidDocInfo;
import com.ejiaoyi.api.dto.v1.GetBaseKey;
import com.ejiaoyi.api.dto.v1.OpenBidRecordInfo;
import com.ejiaoyi.api.enums.ApiCode;
import com.ejiaoyi.api.model.BSNBaseModel;
import com.ejiaoyi.api.model.chaincode.BlockInfoBean;
import com.ejiaoyi.api.model.chaincode.ResModel;
import com.ejiaoyi.api.service.IChainCodeService;
import com.ejiaoyi.api.service.impl.CommonServiceImpl;
import com.ejiaoyi.api.util.SignUtil;
import com.ejiaoyi.api.util.exception.GlobalException;
import com.ejiaoyi.common.annotation.ApiAuthentication;
import com.ejiaoyi.common.annotation.RedissonLock;
import com.ejiaoyi.common.enums.TimeFormatter;
import com.ejiaoyi.common.util.DateTimeUtil;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.commons.lang3.RandomUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.web.bind.annotation.*;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * 区块链接口
 *
 * @author
 * @since 2020/5/11
 */
@Api(value = "区块链接口", tags = "开评标信息上链接口")
@RestController
@RequestMapping("/chainCode")
@Configuration
public class ChainCodeController implements BaseController {

    private final static Logger logger = LoggerFactory.getLogger(ChainCodeController.class);

    private static final String API_NAME = "chainCode";


    @Value("${userCode}")
    private String userCode;

    @Value("${appCode}")
    private String appCode;

    @Value("${chainCode}")
    private String chainCode;

    @Value("${tid}")
    private String tid;

    @Value("${private_cert_path}")
    private String privateCertPath;

    @Autowired
    private IChainCodeService chainCodeService;

    @Autowired
    private CommonServiceImpl commonService;


    @PostMapping("/getToken")
    @ApiOperation(value = "获取TOKEN", notes = "获取TOKEN(token有效时间5分钟)")
    @ApiOperationSupport(
            order = 2,
            author = "author_undefined"
    )
    @ApiAuthentication(apiName = API_NAME, replay = false)
    public RestObject<String> getToken(@RequestBody @ApiParam(name = "JSON", value = "JSON", required = true) GetToken getToken) {
        RestObject<String> restObject = new RestObject<>();
        try {
            String token = chainCodeService.getToken(getToken);
            restObject.setCode(ApiCode.SUCCESS);
            restObject.setData(token);
        } catch (Exception e) {
            e.printStackTrace();
            restObject.setCode(ApiCode.ERROR);
        }
        return restObject;
    }


    @PostMapping("/bidDocumentsWitness")
    @ApiOperation(value = "投标文件存证", notes = "投标文件存证")
    @ApiOperationSupport(
            order = 2,
            author = "author_undefined"
    )
    @ApiAuthentication(apiName = API_NAME)
    public RestObject<BlockInfoBean> bidDocumentsWitness(@RequestBody @ApiParam(name = "JSON", value = "JSON", required = true) ChainBidDocInfo chainBidDocInfo) {
        RestObject<BlockInfoBean> restObject = new RestObject<>();
        try {
            logger.info("保存数据......【{}】", chainBidDocInfo.toString());

            List<String> list = new ArrayList<String>();
            BSNBaseModel bsnBaseModel=addBasekey(chainBidDocInfo);
            list.add(JSONObject.toJSONString(bsnBaseModel));
            String functionName = "set";
            String signStr = getStringSignValue(list, functionName);
            // 封装报文
            Map<String, Object> params = handleApiParam(functionName, list, signStr);
            // 发送请求
            logger.info("保存数据上链开始......");

            ResModel result = commonService.req(params, signStr);
            logger.info("保存上链响应：【{}】", result.toString());
            // 处理结果
            if (result.getBody().getCcRes().getCcCode() == 200) {
                restObject.setCode(ApiCode.SUCCESS);
                restObject.setMsg("保存成功");
                BlockInfoBean blockInfoBean=new BlockInfoBean();
                blockInfoBean.setBaseKey(bsnBaseModel.getBaseKey());
                restObject.setData(blockInfoBean);
                return restObject;
            }
            restObject.setCode(ApiCode.ERROR);

        } catch (Exception e) {
            e.printStackTrace();
            restObject.setCode(ApiCode.ERROR);

        }
        return restObject;
    }



    @PostMapping("/bidderDeclassifiedRecords")
    @ApiOperation(value = "投标人解密记录", notes = "投标人解密记录")
    @ApiOperationSupport(
            order = 3,
            author = "author_undefined"
    )
    @ApiAuthentication(apiName = API_NAME)
    public RestObject<BlockInfoBean> bidderDeclassifiedRecords(@RequestBody @ApiParam(name = "JSON", value = "JSON", required = true)BidderDecrypRecordInfo bidderDecrypRecordInfo) {
        RestObject<BlockInfoBean> restObject = new RestObject<>();
        try {
            logger.info("保存数据......【{}】", bidderDecrypRecordInfo.toString());

            List<String> list = new ArrayList<String>();
            BSNBaseModel bsnBaseModel=addBasekey(bidderDecrypRecordInfo);
            list.add(JSONObject.toJSONString(bsnBaseModel));
            String functionName = "set";
            String signStr = getStringSignValue(list, functionName);
            // 封装报文
            Map<String, Object> params = handleApiParam(functionName, list, signStr);
            // 发送请求
            logger.info("保存数据上链开始......");

            ResModel result = commonService.req(params, signStr);
            logger.info("保存上链响应：【{}】", result.toString());
            // 处理结果
            if (result.getBody().getCcRes().getCcCode() == 200) {
                restObject.setCode(ApiCode.SUCCESS);
                restObject.setMsg("保存成功");
                BlockInfoBean blockInfoBean=result.getBody().getBlockInfo();
                if(blockInfoBean==null){
                    blockInfoBean=new BlockInfoBean();
                }
                blockInfoBean.setBaseKey(bsnBaseModel.getBaseKey());
                restObject.setData(blockInfoBean);
                return restObject;
            }
            restObject.setCode(ApiCode.ERROR);

        } catch (Exception e) {
            e.printStackTrace();
            restObject.setCode(ApiCode.ERROR);

        }
        return restObject;
    }


    @PostMapping("/bid_opening_record")
    @ApiOperation(value = "开标记录表", notes = "开标记录表")
    @ApiOperationSupport(
            order = 4,
            author = "author_undefined"
    )
    @ApiAuthentication(apiName = API_NAME)
    public RestObject<BlockInfoBean> bidOpeningRecord(@RequestBody @ApiParam(name = "JSON", value = "JSON", required = true) OpenBidRecordInfo openBidRecordInfo) {
        RestObject<BlockInfoBean> restObject = new RestObject<>();
        try {
            logger.info("保存数据......【{}】", JSONObject.toJSONString(openBidRecordInfo));

            List<String> list = new ArrayList<String>();
            BSNBaseModel bsnBaseModel=addBasekey(openBidRecordInfo);
            list.add(JSONObject.toJSONString(bsnBaseModel));
            String functionName = "set";
            String signStr = getStringSignValue(list, functionName);
            // 封装报文
            Map<String, Object> params = handleApiParam(functionName, list, signStr);
            // 发送请求
            logger.info("保存数据上链开始......");

            ResModel result = commonService.req(params, signStr);
            logger.info("保存上链响应：【{}】", result.toString());
            // 处理结果
            if (result.getBody().getCcRes().getCcCode() == 200) {
                restObject.setCode(ApiCode.SUCCESS);
                restObject.setMsg("保存成功");
                BlockInfoBean blockInfoBean=result.getBody().getBlockInfo();
                if(blockInfoBean==null){
                   blockInfoBean=new BlockInfoBean();
                }
                blockInfoBean.setBaseKey(bsnBaseModel.getBaseKey());
                restObject.setData(blockInfoBean);
                return restObject;
            }
            restObject.setCode(ApiCode.ERROR);

        } catch (Exception e) {
            e.printStackTrace();
            restObject.setCode(ApiCode.ERROR);

        }
        return restObject;
    }



    @PostMapping("/getChainData")
    @ApiOperation(value = "获取链上数据", notes = "通过唯一标识（baseKey）获取链上数据,返回data为对应的JSON存储值")
    @ApiOperationSupport(
            order = 5,
            author = "author_undefined"
    )
    @ApiAuthentication(apiName = API_NAME)
    public RestObject<String> getChainData(@RequestBody @ApiParam(name = "JSON", value = "JSON", required = true) GetBaseKey getBaseKey) {
        RestObject<String> restObject = new RestObject<>();
        logger.info("获取数据......【{}】", getBaseKey);
        try {
            List<String> list = new ArrayList<String>();
            list.add(getBaseKey.getBaseKey());
            String functionName = "get";
            String signStr = getStringSignValue(list, functionName);
            // 封装报文
            Map<String, Object> params = handleApiParam(functionName, list, signStr);
            // 发送请求
            logger.info("获取上链数据开始......");
            ResModel result = commonService.req(params, signStr);
            logger.info("获取上链数据响应：", result.toString());
            // 处理结果
            if (result.getBody().getCcRes().getCcCode() == 200) {
                restObject.setCode(ApiCode.SUCCESS);
                restObject.setMsg("获取成功");
                restObject.setData(result.getBody().getCcRes().getCcData());
            }else {
                restObject.setCode(ApiCode.ERROR);
            }
        } catch (GlobalException e) {
            restObject.setCode(ApiCode.ERROR);
            restObject.setMsg(e.getMessage());
        }
        return restObject;
    }


    /**
     * @param functionName 方法名
     * @param args         请求参数
     * @return java.util.Map<java.lang.String, java.lang.Object>
     * @description：封装请求网关参数信息
     * @created by 2019/7/5 17:47
     */
    private Map<String, Object> handleApiParam(String functionName, List<String> args, String signStr) {

        Map<String, Object> map = new HashMap<String, Object>();
        Map<String, Object> header = new HashMap<String, Object>();
        header.put("userCode", userCode);
        header.put("appCode", appCode);
        header.put("tId", tid);
        map.put("header", header);

        Map<String, Object> body = new HashMap<String, Object>();
        body.put("chainCode", chainCode);
        body.put("funcName", functionName);

        body.put("args", args);

        map.put("body", body);

        String mac = signMac(signStr);
        System.out.println(signStr);
        System.out.println(mac);

        map.put("mac", mac);

        return map;
    }

    /**
     * @param signStr 签名源数据
     * @return java.lang.String
     * @description：数据签名
     * @created by 2019/7/5 17:48
     */
    private String signMac(String signStr) {
        try {
            Resource keystoreResource = new ClassPathResource(privateCertPath);
            File keystoreFile = keystoreResource.getFile();
            return SignUtil.signByPKPath(keystoreFile.getAbsolutePath(), signStr);
        } catch (Exception e) {
            logger.error("签名失败：", e);
            e.printStackTrace();
        }

        return "";
    }

    /**
     * 生成请求报文的原始加密数据
     *
     * @param list         参数信息
     * @param functionName 方法名称
     * @return
     */
    private String getStringSignValue(List<String> list, String functionName) {
        StringBuffer signStr = new StringBuffer();
        signStr.append(userCode);
        signStr.append(appCode);
        signStr.append(chainCode);
        signStr.append(functionName);
        if (list != null && list.size() > 0) {
            for (int i = 0; i < list.size(); i++) {
                signStr.append(list.get(i));
            }
        }
        return signStr.toString();
    }

    //数据添加唯一索引信息
    @RedissonLock
    private BSNBaseModel addBasekey(Object obj){
        BSNBaseModel baseModel=new BSNBaseModel();
        String basekey="SZFJ"+ DateTimeUtil.getInternetTime(TimeFormatter.YYYYHHDDHHMMSS)+ RandomUtils.nextInt(1000, 9999);
        baseModel.setBaseKey(basekey);
        baseModel.setBaseValue(JSONObject.toJSONString(obj));
        return  baseModel;
    }

}
